#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

static int width;
static int height;
static unsigned short *screen_base = NULL;

#define argb888_to_rgb565(color) ({ \
    unsigned int temp = (color); \
    ((temp & 0xf80000UL) >> 8) | \
    ((temp & 0x00fc00UL) >> 5) | \
    ((temp & 0x0000f8UL) >> 3);\
    }) 


#define argb8888_to_rgb565(color) ({ \
            unsigned int temp = (color); \
            ((temp & 0xF80000UL) >> 8) | \
            ((temp & 0xFC00UL) >> 5) | \
            ((temp & 0xF8UL) >> 3); \
            })


static void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color) 
{
    unsigned short rgb565_color = argb888_to_rgb565(color);

    if (x >= width) {
        x = width - 1;
    }
    if (y >= height) {
        y = height - 1;
    }

    /* 颜色填充 */
    screen_base[y * width + x] = rgb565_color;
}

/* 
 * dir：1->水平  0->垂直 */
static void lcd_draw_line(unsigned int x, unsigned int y, int dir,
                     unsigned int length, unsigned int color)
{
    unsigned short rgb565_color = argb888_to_rgb565(color);

    unsigned int end;
    unsigned long temp;

    if (x >= width) {
        x = width -1;
    }
    if (y >= height) {
        y = height -1;
    }

    temp = y * width + x;
    if (dir) {      // 水平线
        end = x + length - 1;
        if (end >= width) {
            end = width - 1;
        }

        for (; x <= end; x++, temp++) {
            screen_base[temp] = rgb565_color;
        }
    } else {            // 垂直线
        end = y + length - 1;
        if (end >= height) {
            end = height - 1;
        }

        for (; y <= end; y++, temp += width) {   // 跨度为一个宽度的跨度
            screen_base[temp] = rgb565_color;
        }
    }
}

/* 画矩形 */
static void lcd_draw_rectangle(
    unsigned int start_x, unsigned int start_y,
    unsigned int end_x, unsigned int end_y,
    unsigned int color)
{
    unsigned short rgb565_color = argb888_to_rgb565(color);
    int x_len = end_x - start_x + 1;
    int y_len = end_y - start_y + 1;

    lcd_draw_line(start_x, start_y, 1, x_len, rgb565_color);       // 上
    lcd_draw_line(start_x, end_y, 1, x_len, rgb565_color);         // 下
    lcd_draw_line(start_x, start_y + 1, 0, y_len, rgb565_color);   // 左
    lcd_draw_line(end_x, start_y + 1, 0, y_len, rgb565_color);     // 右
}

/* 矩形填充 */
static void lcd_fill(    
    unsigned int start_x, unsigned int start_y,
    unsigned int end_x, unsigned int end_y,
    unsigned int color)
{
    unsigned short rgb565_color = argb888_to_rgb565(color);
    
    unsigned long temp;         // temp 类型为long 不是short
    unsigned int x;

    printf("start:(%d, %d); end:(%d, %d)\n",start_x,start_y,end_x,end_y);

    if (end_x >= width) {
        end_x = width - 1;
    }
    if (end_y >= height) {
        end_y = height  - 1;
    }

    temp = start_y * width;
    for (; start_y <= end_y; start_y++, temp += width) {
        for (x = start_x; x <= end_x; x++) {
            screen_base[temp+x] = rgb565_color;
        }
    }
}


int main (int argc, char *argv[]) 
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;

    if ((fd = open("/dev/fb0", O_RDWR)) < 0) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);

    screen_size = fb_fix.line_length * fb_var.yres;     // 屏幕字节数 计算方法是 每行字节数*行数（不是屏幕分辨率 x*y）
    width = fb_var.xres;
    height = fb_var.yres;

    printf("width:%d,height:%d \n",width,height);

    /* 显示缓冲区映射到地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if ((void *)screen_base == MAP_FAILED) {
        perror("mmap error");
        close(fd);
        exit(EXIT_FAILURE);
    }

    /* 画正方形方块 */
    int w = height * 0.25;//方块的宽度为 1/4 屏幕高度
    lcd_fill(0,  0, width-1, height-1, 0x00); //清屏（屏幕显示黑色）
    lcd_fill(0, width-1, 0, height-1, 0xff); //清屏（屏幕显示黑色）
    lcd_fill(0,  0,w, w, 0xFF0000); //红色方块
    lcd_fill(width-w,  0,width-1, w, 0xFF00); //绿色方块
    lcd_fill(0,  height-w, w, height-1, 0xFF); //蓝色方块
    lcd_fill(width-w, height-w, width-1, height-1, 0xFFFF00);//黄色方块
   
    /* 画线: 十字交叉线 */
    lcd_draw_line(0, height * 0.5, 1, width, 0xFFFFFF);//白色线
    lcd_draw_line(width * 0.5, 0, 0, height, 0xFFFFFF);//白色线
    
    /* 画矩形 */
    unsigned int s_x, s_y, e_x, e_y;
    s_x = 0.25 * width;
    s_y = w;
    e_x = width - s_x;
    e_y = height - s_y;
    for ( ; (s_x <= e_x) && (s_y <= e_y);
        s_x+=5, s_y+=5, e_x-=5, e_y-=5)
    lcd_draw_rectangle(s_x,  s_y,e_x, e_y, 0xFFFFFF);

    munmap(screen_base, screen_size);
    close(fd);
    exit(EXIT_SUCCESS);
}